#include <modules\system\win32\win32_system_gadget.h>

namespace DelGE
{
	namespace Modules
	{
		namespace System
		{
			Win32SystemGadget::Win32SystemGadget()
			{
				this->hWnd = NULL;
				this->hDC = NULL;
				this->bHasFocus = true;

				this->bIsMouseInside = false;
			}

			Win32SystemGadget::Win32SystemGadget(HWND hWND)
			{
				this->hWnd = hWnd;
				this->hDC = GetDC(this->hWnd);
				this->bHasFocus = true;

				this->bIsMouseInside = false;
			}

			Win32SystemGadget::~Win32SystemGadget()
			{
				ReleaseDC(this->hWnd, this->hDC);
				DestroyWindow(this->hWnd);

				this->bIsMouseInside = false;
			}

			void Win32SystemGadget::SetSize(int iWidth, int iHeight)
			{
				RECT windowRect;
				DWORD style;

				style = GetWindowLong(this->hWnd, GWL_STYLE);

				this->iWidth = iWidth;
				this->iHeight = iHeight;

				windowRect.left = windowRect.top = 0;
				windowRect.right = iWidth;
				windowRect.bottom = iHeight;

				AdjustWindowRect(&windowRect, style, FALSE);

				SetWindowPos(this->hWnd, NULL, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, SWP_NOMOVE);

				UpdateWindow(this->hWnd);
			}

			void Win32SystemGadget::SetWidth(int iWidth)
			{
				this->SetSize(iWidth, this->GetHeight());
			}

			void Win32SystemGadget::SetHeight(int iHeight)
			{
				this->SetSize(this->GetWidth(), iHeight);
			}

			int Win32SystemGadget::GetWidth()
			{
				/*RECT windowRect;

				GetClientRect(this->hWnd, &windowRect);

				return windowRect.right;*/
				return this->iWidth;
			}

			int Win32SystemGadget::GetHeight()
			{
				/*RECT windowRect;

				GetClientRect(this->hWnd, &windowRect);

				return windowRect.bottom;*/
				return this->iHeight;
			}

			void Win32SystemGadget::SetPosition(int iX, int iY)
			{
				SetWindowPos(this->hWnd, NULL, iX, iY, 0, 0, SWP_NOSIZE);
			}

			void Win32SystemGadget::SetPositionX(int iX)
			{
				this->SetPosition(iX, this->GetPositionY());
			}

			void Win32SystemGadget::SetPositionY(int iY)
			{
				this->SetPosition(this->GetPositionX(), iY);
			}

			int Win32SystemGadget::GetPositionX()
			{
				RECT windowRect;

				GetWindowRect(this->hWnd, &windowRect);

				return windowRect.left;
			}

			int Win32SystemGadget::GetPositionY()
			{
				RECT windowRect;

				GetWindowRect(this->hWnd, &windowRect);

				return windowRect.top;
			}

			int Win32SystemGadget::GetClientPositionX()
			{
				RECT clientRect;
				POINT clientPosition;

				GetClientRect(this->hWnd, &clientRect);
				clientPosition.x = clientRect.left;
				clientPosition.y = clientRect.top;
				ClientToScreen(this->hWnd, &clientPosition);

				return clientPosition.x;
			}

			int Win32SystemGadget::GetClientPositionY()
			{
				RECT clientRect;
				POINT clientPosition;

				GetClientRect(this->hWnd, &clientRect);
				clientPosition.x = clientRect.left;
				clientPosition.y = clientRect.top;
				ClientToScreen(this->hWnd, &clientPosition);

				return clientPosition.y;
			}

			bool Win32SystemGadget::HasFocus()
			{
				return this->bHasFocus;
			}

			void Win32SystemGadget::SetFocus()
			{
				SetForegroundWindow(this->hWnd);
				this->bHasFocus = true;
			}

			bool Win32SystemGadget::IsMouseInside()
			{
				return this->bIsMouseInside;
			}

			int Win32SystemGadget::ScreenPositionXToClientPositionX(int iScreenPositionX)
			{
				return iScreenPositionX - this->GetClientPositionX();
			}

			int Win32SystemGadget::ScreenPositionYToClientPositionY(int iScreenPositionY)
			{
				return iScreenPositionY - this->GetClientPositionY();
			}

			int Win32SystemGadget::ClientPositionXToScreenPositionX(int iClientPositionX)
			{
				return iClientPositionX + this->GetClientPositionX();
			}

			int Win32SystemGadget::ClientPositionYToScreenPositionY(int iClientPositionY)
			{
				return iClientPositionY + this->GetClientPositionY();
			}

			HWND Win32SystemGadget::GetHWND()
			{
				return this->hWnd;
			}

			HDC Win32SystemGadget::GetHDC()
			{
				return this->hDC;
			}

			DelGESystemGadgetType Win32SystemGadget::GetGadgetType()
			{
				return this->eGadgetType;
			}

			KeyCode Win32SystemGadget::ConvertVirtualKeyCode(WPARAM wParam, LPARAM lParam)
			{

				switch (wParam)
				{
				case 'A':				return DelGE_Key_A;
				case 'B':				return DelGE_Key_B;
				case 'C':				return DelGE_Key_C;
				case 'D':				return DelGE_Key_D;
				case 'E':				return DelGE_Key_E;
				case 'F':				return DelGE_Key_F;
				case 'G':				return DelGE_Key_G;
				case 'H':				return DelGE_Key_H;
				case 'I':				return DelGE_Key_I;
				case 'J':				return DelGE_Key_J;
				case 'K':				return DelGE_Key_K;
				case 'L':				return DelGE_Key_L;
				case 'M':				return DelGE_Key_M;
				case 'N':				return DelGE_Key_N;
				case 'O':				return DelGE_Key_O;
				case 'P':				return DelGE_Key_P;
				case 'Q':				return DelGE_Key_Q;
				case 'R':				return DelGE_Key_R;
				case 'S':				return DelGE_Key_S;
				case 'T':				return DelGE_Key_T;
				case 'U':				return DelGE_Key_U;
				case 'V':				return DelGE_Key_V;
				case 'W':				return DelGE_Key_W;
				case 'X':				return DelGE_Key_X;
				case 'Y':				return DelGE_Key_Y;
				case 'Z':				return DelGE_Key_Z;

				case '0':				return DelGE_Key_Num0;
				case '1':				return DelGE_Key_Num1;
				case '2':				return DelGE_Key_Num2;
				case '3':				return DelGE_Key_Num3;
				case '4':				return DelGE_Key_Num4;
				case '5':				return DelGE_Key_Num5;
				case '6':				return DelGE_Key_Num6;
				case '7':				return DelGE_Key_Num7;
				case '8':				return DelGE_Key_Num8;
				case '9':				return DelGE_Key_Num9;

				case VK_NUMPAD0:		return DelGE_Key_NumPad0;
				case VK_NUMPAD1:		return DelGE_Key_NumPad1;
				case VK_NUMPAD2:		return DelGE_Key_NumPad2;
				case VK_NUMPAD3:		return DelGE_Key_NumPad3;
				case VK_NUMPAD4:		return DelGE_Key_NumPad4;
				case VK_NUMPAD5:		return DelGE_Key_NumPad5;
				case VK_NUMPAD6:		return DelGE_Key_NumPad6;
				case VK_NUMPAD7:		return DelGE_Key_NumPad7;
				case VK_NUMPAD8:		return DelGE_Key_NumPad8;
				case VK_NUMPAD9:		return DelGE_Key_NumPad9;

				case VK_UP:				return DelGE_Key_Up;
				case VK_DOWN:			return DelGE_Key_Down;
				case VK_LEFT:			return DelGE_Key_Left;
				case VK_RIGHT:			return DelGE_Key_Right;

				case VK_F1:				return DelGE_Key_F1;
				case VK_F2:				return DelGE_Key_F2;
				case VK_F3:				return DelGE_Key_F3;
				case VK_F4:				return DelGE_Key_F4;
				case VK_F5:				return DelGE_Key_F5;
				case VK_F6:				return DelGE_Key_F6;
				case VK_F7:				return DelGE_Key_F7;
				case VK_F8:				return DelGE_Key_F8;
				case VK_F9:				return DelGE_Key_F9;
				case VK_F10:			return DelGE_Key_F10;
				case VK_F11:			return DelGE_Key_F11;
				case VK_F12:			return DelGE_Key_F12;

				case VK_LWIN:			return DelGE_Key_LeftSystem;
				case VK_RWIN:			return DelGE_Key_RightSystem;

				case VK_OEM_1:			return DelGE_Key_Semicolon;
				case VK_OEM_2:			return DelGE_Key_Slash;
				case VK_OEM_3:			return DelGE_Key_Tilde;
				case VK_OEM_4:			return DelGE_Key_OpenBracket;
				case VK_OEM_5:			return DelGE_Key_Backslash;
				case VK_OEM_6:			return DelGE_Key_CloseBracket;
				case VK_OEM_7:			return DelGE_Key_Quotes;


				case VK_ESCAPE:			return DelGE_Key_Escape;
				case VK_SPACE:			return DelGE_Key_Space;
				case VK_RETURN:			return DelGE_Key_Enter;
				case VK_TAB:			return DelGE_Key_Tab;
				case VK_PRIOR:			return DelGE_Key_PageUp;
				case VK_NEXT:			return DelGE_Key_PageDown;
				case VK_END:			return DelGE_Key_End;
				case VK_HOME:			return DelGE_Key_Home;
				case VK_INSERT:			return DelGE_Key_Insert;
				case VK_DELETE:			return DelGE_Key_Delete;
				case VK_ADD:			return DelGE_Key_NumPadAdd;
				case VK_SUBTRACT:		return DelGE_Key_NumPadSubtract;
				case VK_MULTIPLY:		return DelGE_Key_NumPadMultiply;
				case VK_DIVIDE:			return DelGE_Key_NumPadDivide;

				case VK_CONTROL:		if (HIWORD(lParam) & KF_EXTENDED) return DelGE_Key_RightControl; return DelGE_Key_LeftControl;
				case VK_MENU:			if (HIWORD(lParam) & KF_EXTENDED) return DelGE_Key_RightAlt; return DelGE_Key_LeftAlt;
				case VK_SHIFT:
					UINT uiShift = MapVirtualKey(VK_LSHIFT, MAPVK_VK_TO_VSC);
					if (((lParam & 0x01FF0000) >> 16) == uiShift)
						return DelGE_Key_RightShift;
					return DelGE_Key_LeftShift;
				}

				return KeyCode(0);
			}

			void Win32SystemGadget::ProcessEvent(UINT msg, WPARAM wParam, LPARAM lParam)
			{
				switch (msg)
				{
				case WM_DESTROY:
					{
						ReleaseDC(this->hWnd, this->hDC);
						DestroyWindow(this->hWnd);
						break;
					}

				case WM_CLOSE:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_Close;
						oEvent.WindowData.pWindow = this;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_SETFOCUS:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_GainedFocus;
						oEvent.WindowData.pWindow = this;
						EventManager::PushEvent(oEvent);
						this->bHasFocus = true;
						break;
					}

				case WM_KILLFOCUS:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_LostFocus;
						oEvent.WindowData.pWindow = this;
						EventManager::PushEvent(oEvent);
						this->bHasFocus = false;
						break;
					}

				case WM_CHAR: case WM_SYSCHAR:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_CharacterInput;
						oEvent.CharacterInputData.pWindow = this;
						oEvent.CharacterInputData.cCharacterInput = (char)wParam;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_KEYDOWN: case WM_SYSKEYDOWN:
                    {
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_KeyPressed;
						oEvent.KeybordData.pWindow = this;
						oEvent.KeybordData.eKeyCode = ConvertVirtualKeyCode(wParam, lParam);
						EventManager::PushEvent(oEvent);
						break;
                    }
				case WM_KEYUP: case WM_SYSKEYUP:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_KeyReleased;
						oEvent.KeybordData.pWindow = this;
						oEvent.KeybordData.eKeyCode = ConvertVirtualKeyCode(wParam, lParam);
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_LBUTTONDOWN:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_MousePressed;
						oEvent.MouseData.eMouseButton = DelGE_Mouse_Left;
						oEvent.MouseData.iMouseX = ((int)(LOWORD(lParam)));
						oEvent.MouseData.iMouseY = ((int)(HIWORD(lParam)));
						oEvent.MouseData.iMouseWheelDelta = 0;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_RBUTTONDOWN:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_MousePressed;
						oEvent.MouseData.eMouseButton = DelGE_Mouse_Right;
						oEvent.MouseData.iMouseX = ((int)(LOWORD(lParam)));
						oEvent.MouseData.iMouseY = ((int)(HIWORD(lParam)));
						oEvent.MouseData.iMouseWheelDelta = 0;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_MBUTTONDOWN:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_MousePressed;
						oEvent.MouseData.eMouseButton = DelGE_Mouse_Middle;
						oEvent.MouseData.iMouseX = ((int)(LOWORD(lParam)));
						oEvent.MouseData.iMouseY = ((int)(HIWORD(lParam)));
						oEvent.MouseData.iMouseWheelDelta = 0;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_LBUTTONUP:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_MouseReleased;
						oEvent.MouseData.eMouseButton = DelGE_Mouse_Left;
						oEvent.MouseData.iMouseX = ((int)(LOWORD(lParam)));
						oEvent.MouseData.iMouseY = ((int)(HIWORD(lParam)));
						oEvent.MouseData.iMouseWheelDelta = 0;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_RBUTTONUP:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_MouseReleased;
						oEvent.MouseData.eMouseButton = DelGE_Mouse_Right;
						oEvent.MouseData.iMouseX = ((int)(LOWORD(lParam)));
						oEvent.MouseData.iMouseY = ((int)(HIWORD(lParam)));
						oEvent.MouseData.iMouseWheelDelta = 0;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_MBUTTONUP:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_MouseReleased;
						oEvent.MouseData.eMouseButton = DelGE_Mouse_Middle;
						oEvent.MouseData.iMouseX = ((int)(LOWORD(lParam)));
						oEvent.MouseData.iMouseY = ((int)(HIWORD(lParam)));
						oEvent.MouseData.iMouseWheelDelta = 0;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_MOUSEMOVE:
					{
						if (!this->bIsMouseInside)
						{
							this->bIsMouseInside = true;

							TRACKMOUSEEVENT trackMouseEvent = { sizeof(trackMouseEvent) };
							trackMouseEvent.dwFlags = TME_LEAVE;
							trackMouseEvent.hwndTrack = this->hWnd;
							TrackMouseEvent(&trackMouseEvent);

							DelGEEvent oEvent;
							oEvent.eType = DelGE_Event_SystemWindow_MouseEnter;
							oEvent.MouseData.pWindow = this;
							EventManager::PushEvent(oEvent);
						}

						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_MouseMoved;
						oEvent.MouseData.eMouseButton = DelGE_Mouse_Unknown;
						oEvent.MouseData.iMouseX = ((int)(LOWORD(lParam)));
						oEvent.MouseData.iMouseY = ((int)(HIWORD(lParam)));
						oEvent.MouseData.iMouseWheelDelta = 0;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_MOUSELEAVE:
					{
						this->bIsMouseInside = false;
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_MouseLeave;
						oEvent.MouseData.pWindow = this;
						EventManager::PushEvent(oEvent);
						break;
					}

				case WM_MOUSEWHEEL:
					{
						DelGEEvent oEvent;
						oEvent.eType = DelGE_Event_SystemWindow_MouseWheelMoved;
						oEvent.MouseData.eMouseButton = DelGE_Mouse_Unknown;
						oEvent.MouseData.iMouseX = ScreenPositionXToClientPositionX(((int)(LOWORD(lParam))));
						oEvent.MouseData.iMouseY = ScreenPositionYToClientPositionY(((int)(HIWORD(lParam))));
						oEvent.MouseData.iMouseWheelDelta = (GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA);
						EventManager::PushEvent(oEvent);
						break;
					}
				}
			}
		}
	}
}
